<?php

/**
 * session抽象库
 *
 * session接口类，内部按不同方式实例化一个对象，实现方法有多个，但接口都一样。
 * $this->_sessionObject->get($key);
 * $this->_sessionObject->set($key);
 * 等等。
 * 这是较好的策略工厂模式，但session不推荐这样实现，参考eshequn实现。其实就两个操作：
 * 1.当请求session服务时，把上次会话数据调出来（从文件或数据库或memcache）;
 * 2.当会话结束后，把本次session数据存入存储;
 * 因此，最好是采用
 */

class Session
{
    private $_SESSION     = array();

    private $session_name = '';
    private $session_id   = '';

    private $_sess_active = NULL;
    private $_sess_hash   = NULL;
    private $_project_id      = NULL;
    private $_app_id      = NULL;
    private $_ip          = NULL;

    private $inited       = false;

    private $session_cookie_path   = '/';
    private $session_cookie_domain = '';
    private $session_cookie_secure = false;
    private $_sessionStorage = NULL;

    // $storage 存储介质
    public function __construct($project_id, $storage, $session_name = '', $session_id = '')
    {
        $this->_project_id  = $project_id;
        $this->_app_id      = get_app_id(APP);
        switch ($storage)
        {
            case 'database':
                require_once('database_session.class.php');
                $this->_sessionStorage = new DB_Session();
                break;
            case 'memcache':
                require_once('memcache_session.class.php');
                $this->_sessionStorage = new Memcache_Session();
                break;
            default:
                throw new systemException('invalid session storage');
                break;
        }

        if ($session_name)
        {
            $this->session_name = $session_name;
        }
        else
        {
            $this->session_name = PROJECT . '_SESSID';
        }

        $cookie_session_id  = fetch_cookie($this->session_name, 'trim');

        if ($session_id === '' && $cookie_session_id !== '')
        {
            $this->session_id = $cookie_session_id;
        }
        else
        {
            $this->session_id = $session_id;
        }

        $this->_ip = $GLOBALS['REQUEST_INFO']['client_ip'];

        if ($this->session_id)
        {
            $tmp_session_id = substr($this->session_id, 0, 32);
            if ($this->gen_session_key($tmp_session_id) === substr($this->session_id, 32))
            {
                $this->session_id = $tmp_session_id;
            }
            else
            {
                $this->session_id = '';
            }
        }
    }

    public function __destruct()
    {
        if ($this->inited === true)
        {
            $this->_session_storage->save_session($this->_SESSION);
        }
    }

    public function __set($key, $value)
    {
        if ($this->inited === false)
        {
            $this->init();
        }

        $this->_SESSION[$key] = $value;
    }

    public function &__get($key)
    {
        // 如果初次取数据，先从数据源取出所有SESSION值
        if ($this->inited === false)
        {
            $this->init();
        }

        return $this->_SESSION[$key];
    }

    public function __isset($key)
    {
        if ($this->inited === false)
        {
            $this->init();
        }

        return isset($this->_SESSION[$key]);
    }

    public function __unset($key)
    {
        if ($this->inited === false)
        {
            $this->init();
        }

        unset($this->_SESSION[$key]);
    }

    private function init()
    {
        if (!empty($GLOBALS['cookie_path']))
        {
            $this->session_cookie_path = $GLOBALS['cookie_path'];
        }
        else
        {
            $this->session_cookie_path = '/';
        }

        if (!empty($GLOBALS['cookie_domain']))
        {
            $this->session_cookie_domain = $GLOBALS['cookie_domain'];
        }
        else
        {
            if (defined('MAIN_DOMAIN') === true)
            {
                $this->session_cookie_domain = '.' . MAIN_DOMAIN;
            }
            else
            {
                $this->session_cookie_domain = '';
            }
        }

        if (!empty($GLOBALS['cookie_secure']))
        {
            $this->session_cookie_secure = $GLOBALS['cookie_secure'];
        }
        else
        {
            $this->session_cookie_secure = false;
        }

        if ($this->session_id !== '')
        {
            $this->_SESSION = $this->_session_storage->fetch_session();
            /**
             *  todo: _sess_active是否到期
             *  $this->_sess_active = (int)$expiry;
             *  $this->_sess_hash   = (int)$hash + crc32($result);
             */
        }
        else
        {
            $this->session_id   = $this->gen_session_id();
            $this->_sess_hash   = ($this->_project_id + $this->_app_id - 1571559392);
            $this->_sess_active = 0;

            setcookie($this->session_name, $this->session_id . $this->gen_session_key($this->session_id), 0, $this->session_cookie_path, $this->session_cookie_domain, $this->session_cookie_secure);
        }

        $this->inited = true;
    }

    private function gen_session_id()
    {
        /* 更好的避免碰撞问题 */
        return md5(microtime(true) . uniqid(mt_rand(), true));
    }

    private function gen_session_key($session_id)
    {
        /* 对 SESSION 进行认证 */
        return sprintf('%08x', crc32($_SERVER['HTTP_USER_AGENT'] . ROOT_PATH . $session_id));
    }

    public function update_session()
    {
        $this->_session_storage->save_session($this->_SESSION);
    }

    public function destroy_session()
    {
        require_module_config('framework.session');

        $params = 'cmd=DELETE&site_id=' . $this->_project_id . '&sess_id=' . $this->session_id;
        $result = self::_session_request($GLOBALS['session_module_settings']['api_url'], $params);

        setcookie($this->session_name, '', -1, $this->session_cookie_path, $this->session_cookie_domain, $this->session_cookie_secure);

        /* 变量重新初始化 */
        $this->_SESSION   = array();
        $this->session_id = '';
        $this->inited     = false;
    }

    public function get_session_id()
    {
        if ($this->inited === false)
        {
            $this->init();
        }

        return $this->session_id;
    }
}

?>